package fivecardstudd;
import java.util.*;

import fivecardstudd.Player.RoundStatus;


public class Table {

	 Scanner input=new Scanner(System.in);
     
 
     public enum TableStatus{
             IN_GAME, DONE;
     }
     private static int numberOfTables = 0;

     private int tableID;
     private ArrayList<AI> aiPlayers;
     private ArrayList<Player> players;
     private HashMap<String, Player> playerHash;
     private HashMap<String, AI> aiHash;
     private int numberOfPlayers;
     private TableStatus status;
     private int ante=20;
     private Deck deck;
     private int currentPot;
     private Object currentTurn;
     private int currentBet=20;
     private final int MAX_BET=200;
     private final int MAX_PLAYER=4;


     public Table(String name) {             
             tableID = numberOfTables + 1;
             numberOfTables++;
             setStatus(TableStatus.DONE);
             aiPlayers= new ArrayList<AI>();
             players = new ArrayList<Player>();
             playerHash = new HashMap<String, Player>();
             aiHash=new HashMap<String,AI>();
             numberOfPlayers = 0;
             deck = new Deck();
     
         
     }
     
     public int getNumberOfPlayers(){
    	 return numberOfPlayers;
     }
     
     
     public void addAIPlayer(AI aiplayer){
    	 while(!this.status.equals(TableStatus.DONE)){
             
         }
       
         if (aiplayer.getActive())
                 aiplayer.fold();

         aiPlayers.add(aiplayer);
         aiHash.put(aiplayer.getAIname(), aiplayer);
         numberOfPlayers++;
         
     }
    
     public void removeAIPlayer(AI aiplayer){
    	 while(!this.status.equals(TableStatus.DONE)){
             
         }
       
         if (aiplayer.getActive())
                 aiplayer.fold();

         aiPlayers.remove(aiplayer);
         playerHash.remove(aiplayer.getAIname());
         numberOfPlayers--;
         
     }

     
     public ArrayList<AI> getAIPlayers(){
    	 return aiPlayers;
     }
     
     public int getTableID() {
             return tableID;
     }

 
     public ArrayList<Player> getPlayers() {
             return players;
     }

     
     public int getCurrentPot() {
             return currentPot;
     }

     public void setCurrentPot(int currentPot) {
             this.currentPot = currentPot;
     }

     public Object getCurrentTurn() {
             return currentTurn;
     }

     public void setCurrentTurn(Object currentTurn) {
             this.currentTurn = currentTurn;
     }

     public int getCurrentBet() {
             return currentBet;
     }

     public void setCurrentBet(int currentBet) {
             this.currentBet = currentBet;
     }

     public int getAnte() {
             return ante;
     }

     public int getMaxBet() {
             return MAX_BET;
     }
     
     public TableStatus getStatus() {
             return status;
     }

     public void setStatus(TableStatus status) {
             this.status = status;
     }
     

   
     public String getPlayerName(int seat) {
             if (seat >= players.size()) {
                     return null;
             }
             return players.get(seat).getName();
     }

     public void updateTableStatus() {
             if (this.isActive()) {
                     // Log!
             }
             else if (this.numberOfPlayers < 2) {
                     System.out.println("Waiting for " + (2 - this.numberOfPlayers) + " tablePlayers...");
             }
             else {
                     this.setStatus(TableStatus.IN_GAME);
                     for(Player player: players){
                             player.setActive();
                     }
                     initialiseHand();
             }
     }
     
     public boolean isActive() {
             return status == TableStatus.IN_GAME;
     }
     
     public boolean isDone(){
             return status == TableStatus.DONE;
     }

     
     
     public void addPlayer(Player player){
             while(!this.status.equals(TableStatus.DONE)){
           
             }
             if (player.getActive())
                     player.fold();

             players.add(player);
             playerHash.put(player.getName(), player);
             numberOfPlayers++;
     }
     

     public Player getPlayer(String username) {
             return playerHash.get(username);
     }
     

     public void removePlayer(Player player) {
             while(!this.status.equals(TableStatus.DONE)){
                     //wait for current round to end to end
             }
             
             players.remove(player);         
             playerHash.remove(player.getName());
             numberOfPlayers--;
     }

   
     public boolean isFull() {
             if (this.numberOfPlayers == MAX_PLAYER) {
                     return true;
             }
             else {
                     return false;
             }
     }
     

     public void dealFaceUp(boolean faceUp){
             Hand playerHand;
             Card currentCard;
             Hand AIhand;
             
           ArrayList<Object> AllPlayers=new ArrayList<Object>();
           for(Player player:players){
        	   AllPlayers.add(player);
           }
           for(AI ai:aiPlayers){
        	   AllPlayers.add(ai);
        	 }
           for(Object unknownObject:AllPlayers){
        	   if(unknownObject instanceof Player){
        		   Player player=(Player)unknownObject;
        		   playerHand=player.getCurrentHand();
        		   currentCard = deck.drawCard(); 
                   currentCard.setFaceUp(faceUp);
                   playerHand.addCard(currentCard);
                   System.out.println("Player " + player.getName() + " was dealt " + player.getCurrentHand());
               }
        	   else if(unknownObject instanceof AI){
        		   if(unknownObject instanceof AI){
            		   AI aiplayer=(AI)unknownObject;
            		   AIhand=aiplayer.getCurrentHand();
            		   currentCard = deck.drawCard(); 
                       currentCard.setFaceUp(faceUp);
                       AIhand.addCard(currentCard);
                       System.out.println("Player " + aiplayer.getAIname() + " was dealt " + aiplayer.getCurrentHand());
                   } 
        	   }
        	   }
           }
     
     
   
     public void increasePot(int chips){
             currentPot += chips;
     }
     
     
     public void increaseBet(int betValue){
             currentBet += betValue;
     }
     
    
     public int countActivePlayers(){
             int count = 0;
             for(Player player: players){
                     if(player.getActive())
                             count++;
             }
             for(AI aiplayer:aiPlayers){
            	 if(aiplayer.getActive())
            		 count++;
             }
             return count;
     }
     
   
     public void setTurn(Object player){
            if(player instanceof Player){
            	Player player1=(Player)player;
            	 player1.setTurn();
                 setCurrentTurn(player1);
            }
            if(player instanceof AI){
            	AI aiplayer=(AI)player;
            	 aiplayer.setTurn();
                 setCurrentTurn(aiplayer);
            }
     }
     
    
     public int currentTurnIndex(){
             int index=-1;
             ArrayList<Object> AllPlayers=new ArrayList<Object>();
             for(Player player:players){
            	 AllPlayers.add(player);
             }
             for(AI aiplayer:aiPlayers){
            	 AllPlayers.add(aiplayer);
             }
             
             for(int i=0; i<AllPlayers.size(); i++){
                 if(AllPlayers.get(i)instanceof Player)   {
                	 Player player=(Player)AllPlayers.get(i);
                	 if(player.inTurn())
                		 index=i;
                 }  
                 else if(AllPlayers.get(i)instanceof AI)   {
                	 AI aiplayer=(AI)AllPlayers.get(i);
                	 if(aiplayer.inTurn())
                		 index=i;
                 } 
             }
             return index;
     }
     
    
    
     
     
     public void setNextTurn(){
             int currentIndex = currentTurnIndex();
            if(getCurrentTurn() instanceof Player){
            	Player player=(Player)getCurrentTurn();
            	player.setActive();
            }
            else if(getCurrentTurn() instanceof AI){
            	AI aiplayer=(AI)getCurrentTurn();
            	aiplayer.setActive();
            }
            	
             
             setNextTurn(currentIndex);
     }
     
     
     public void setNextTurn(int currentIndex){
         boolean found = false;
         ArrayList<Object> AllPlayers=new ArrayList<Object>();
         for(Player player:players){
        	 AllPlayers.add(player);
         }
         for(AI aiplayer:aiPlayers){
        	 AllPlayers.add(aiplayer);
         }
         
         for(int i=currentIndex+1;i<AllPlayers.size()&&!found;i++){
        	 if(AllPlayers.get(i)instanceof Player){
        		 Player player=(Player)AllPlayers.get(i);
        		 if(player.getActive()){
        			 setTurn(AllPlayers.get(i));
        			 found=true;
        		 }
        	 }
        	 else if(AllPlayers.get(i)instanceof AI){
        		 AI aiplayer=(AI)AllPlayers.get(i);
        		 if(aiplayer.getActive()){
        			 setTurn(AllPlayers.get(i));
        			 found=true;
        		 }
        	 }}
        	//if we exit the loop, we did not find the player, check first half
        	 for(int i=0;i<currentIndex&&!found;i++){
        		 if(AllPlayers.get(i)instanceof Player){
        			 Player player=(Player)AllPlayers.get(i);
        			 if(player.getActive()){
        				 setTurn(player);
        				 found=true;
        			 }
        		 }
        		 
        	 }
        	 if(getCurrentTurn()instanceof Player){
        		 Player player=(Player)getCurrentTurn();
        		 if(player.getChips()==0){
        			 setNextTurn();
        		 }
        	 }
        	 else if(getCurrentTurn()instanceof AI){
        		 AI aiplayer=(AI)getCurrentTurn();
        		 if(aiplayer.getChips()==0){
        			 setNextTurn();
        		 }
        	 }
         }
    
 
     
     
   
     public void resetPlayersCurrentBets(){
        ArrayList<Object> AllPlayers=new ArrayList<Object>();
    	 
    	 for(Player player:players){
    		 AllPlayers.add(player);
    	 }
    	 for(AI player:aiPlayers){
    		 AllPlayers.add(player);
    	 }
    	 
    	 for(Object player:AllPlayers){
    		 if(player instanceof Player){
    			 Player player1=(Player)player;
    			 player1.resetLastBet();
    		 }
    		 else if(player instanceof AI){
    			 AI aiplayer=(AI)player;
    			aiplayer.resetLastBet();
    		 }
    	 }
     }
     
    
     public boolean allFolded(){
             return countActivePlayers() < 2;
     }
     
     
     public void allFaceUp(){
           
    	 ArrayList<Object> AllPlayers=new ArrayList<Object>();
    	 
    	 for(Player player:players){
    		 AllPlayers.add(player);
    	 }
    	 for(AI player:aiPlayers){
    		 AllPlayers.add(player);
    	 }
    	 
    	 for(Object player:AllPlayers){
    		 if(player instanceof Player){
    			 Player player1=(Player)player;
    			 if(player1.getActive()){
    				 player1.getCurrentHand().setFaceUp();
    			 }
    		 }
    		 else if(player instanceof AI){
    			 AI aiplayer=(AI)player;
    			 if(aiplayer.getActive()){
    				 aiplayer.getCurrentHand().setFaceUp();
    			 }
    		 }
    	 }
    
     }
     

     public Object determineFirstBetter(boolean firstRound) {
            Object first=null;
            ArrayList<Object> playerlist=new ArrayList<Object>();
            // add both ai players and human players to object list
            for(Player player:players){
            	playerlist.add(player);
            }
            for(AI aiplayer:aiPlayers){
            	playerlist.add(aiplayer);
            }
            
            
            if(firstRound){
            	int tempLowest=0;
            	for(Object unknownObject:playerlist){
            		
            		if(tempLowest==0){
            			if(unknownObject instanceof Player){
                 		   Player player=(Player)unknownObject;
                 		   tempLowest=player.getValueOfCard(1);
                            first=(Object)player;
                                  }
                 		else if(unknownObject instanceof AI){
                 			AI aiplayer=(AI)unknownObject;
                 			tempLowest=aiplayer.getValueOfCard(1);
                 			first=(Object)aiplayer;
                 		}
            		      
            		}
            		else{
            			if(unknownObject instanceof Player){
                  		   Player player=(Player)unknownObject;
                  		   int temp=player.getValueOfCard(1);
                  		   if(temp<tempLowest){
                  			   tempLowest=temp;
                  			   first=(Object)player;
                  		   }
                                   }
            			else if(unknownObject instanceof AI){
                   		   AI aiplayer=(AI)unknownObject;
                   		   int temp=aiplayer.getValueOfCard(1);
                   		   if(temp<tempLowest){
                   			   tempLowest=temp;
                   			   first=(Object)aiplayer;
                   		   }
                                    }
            			
            			
            		}
            		
            	}
            }  
            else{
            	ArrayList<Object> eligiblePlayers=new ArrayList<Object>();
            	ArrayList<Object> AllPlayers=new ArrayList<Object>();
           	  for(Player player:players){
           		 AllPlayers.add(player);
             	 }
            	 for(AI player:aiPlayers){
           		 AllPlayers.add(player);
           	     }
            	int j=0; 
            	
            	do{
            		eligiblePlayers=bettingList(AllPlayers.get(j));
            		j++;
            	}
            	while(j<AllPlayers.size()&&!getActiveForAll(AllPlayers.get(j)));
            	
            	Object currentLeader=eligiblePlayers.get(0);
            
            	int[] leadingHandRank=getCurrentHand(currentLeader).determineVisibleHandRanking();
            	int[] currentHandRank = new int[6];
            	
            	for(int i=1;i<eligiblePlayers.size();i++){
            		currentHandRank=getCurrentHand(eligiblePlayers.get(i)).determineVisibleHandRanking();
            		if(arrayIsGreater(currentHandRank, leadingHandRank) == 1){
                        //set the current leader to the current player
                        currentLeader = eligiblePlayers.get(i);
                       //set the leading hand rank to current hand rank
                        leadingHandRank = currentHandRank;
            	}
            	first=currentLeader;
            }
            }
            	return first;
            	
            }
            
      public ArrayList<Object> bettingList(Object firstBetter){
    	   ArrayList<Object> bettingList=new ArrayList<Object>();
    	   
    	  ArrayList<Object> allPlayers=new ArrayList<Object>();
    	  
    	  for(Player player:players){
    		  allPlayers.add(player);
    	  }
    	  for(AI aiplayer:aiPlayers){
    		  allPlayers.add(aiplayer);
    	  }
    	  
    	  int startingPoint=allPlayers.lastIndexOf(firstBetter);
    	  
    	  for(int i=startingPoint;i<allPlayers.size();i++){
    	      if(allPlayers.get(i) instanceof Player){
    	    	Player playerObject=(Player)allPlayers.get(i);
    	    	if(playerObject.getActive()){
    	    		bettingList.add(playerObject);
    	    	}
    	    	 }
    	      else if(allPlayers.get(i) instanceof AI){
    	    	  AI aiObject=(AI)allPlayers.get(i);
      	    	if(aiObject.getActive()){
      	    		bettingList.add(aiObject);
      	    	}
    	    	  }
    	  }
    	  
    	  for(int i=0;i<startingPoint;i++){
    	      if(allPlayers.get(i) instanceof Player){
    	    	Player playerObject=(Player)allPlayers.get(i);
    	    	if(playerObject.getActive()){
    	    		bettingList.add(playerObject);
    	    	}
    	    	 }
    	      else if(allPlayers.get(i) instanceof AI){
    	    	  AI aiObject=(AI)allPlayers.get(i);
      	    	if(aiObject.getActive()){
      	    		bettingList.add(aiObject);
      	    	}
    	    	  }
    	  }
    	  
    	  
    	  return bettingList;
      }
     
     
      public Object getWinner() { 
         ArrayList<Object> eligiblePlayers = new ArrayList<Object>();
          
          for(Player player:players){
        	  if(player.getRoundStatus()!=RoundStatus.INACTIVE){
        		  eligiblePlayers.add(player);
        	  }
          }
          for(AI aiplayer:aiPlayers){
        	  if(aiplayer.getActive()){
        		  eligiblePlayers.add(aiplayer);
        	  }
          }
          
          if(eligiblePlayers.size() < 2){
              return eligiblePlayers;
          } 
          
          Object currentLeader = eligiblePlayers.get(0);
          int[] leadingHandRank=getCurrentHand(currentLeader).determineHandRanking();
          int[] currentHandRank = new int[6];
          
          for(int i=1;i<eligiblePlayers.size();i++){
          	currentHandRank=getCurrentHand(eligiblePlayers.get(i)).determineHandRanking();
          	
          	 if(arrayIsGreater(currentHandRank, leadingHandRank) == 1){
              
                   currentLeader = eligiblePlayers.get(i);
                   leadingHandRank = currentHandRank;
           }
          	
   }
                return currentLeader;
  }


    public Hand getCurrentHand(Object a){
    	Hand currentHand=new Hand();
    	
    	if(a instanceof Player){
    	 currentHand=((Player) a).getCurrentHand();
    	}
    	else if(a instanceof AI){
    		currentHand=((AI)a).getCurrentHand();
    	}
    	
    	return currentHand;
    }
    
    
    public void initialiseHand(){   
        
             currentPot = 0;
             currentBet = 0;
          
             for(Player player: players){
                     player.handReset();
                     player.setActive();
                     player.resetLastBet();
             }
             
             for(AI ai:aiPlayers){
            	 ai.handReset();
            	 ai.setActive();
            	 ai.resetLastBet();
            	 
             }
         
             deck = new Deck();
             
             for(Player player: players){
                     player.bet(getAnte());
                     player.resetLastBet();
             }
             
             for(AI ai:aiPlayers){
            	 ai.bet(getAnte());
            	 ai.resetLastBet();
             }
             //set table to IN_GAME
             setStatus(TableStatus.IN_GAME);
             
             // Deal one card face down, and another card face-up.
             dealFaceUp(false);
             dealFaceUp(true);
     }
           
     
     
 
 
     
     
     
     public boolean getActiveForAll(Object member){
    	boolean active =false;
    	 if(member instanceof Player){
    		 Player player=(Player)member;
    		 active=player.getActive();
    		 
    		 	 }
    	 else if(member instanceof AI){
    		 AI aiplayer=(AI)member;
    		active=aiplayer.getActive();
    	 }
    	 return active;
    	 }
     
    
     
     
     private boolean allBetEqual(){
        ArrayList<Object>AllPlayers=new ArrayList<Object>();
        for(Player player:players){
        	AllPlayers.add(player);
        }
        for(AI aiplayer:aiPlayers){
        	AllPlayers.add(aiplayer);
        }
    	 
    	 
        for(Object player:AllPlayers){
        	if(player instanceof Player){
        		Player player1=(Player)player;
        		if(player1.getActive()&&player1.getLastBet()!=currentBet){
        			return false;
        		}
        	}
        	else if(player instanceof AI){
        		AI aiplayer=(AI)player;
        		if(aiplayer.getActive()&&aiplayer.getLastBet()!=currentBet){
        			return false;
        		}
        	}
        	
        }
        return true;
     
 }
     
     
     public void bettingRound(boolean firstBettingRound){
    	 
    	 Object firstBetter=determineFirstBetter(firstBettingRound);
    	 
    	 if(firstBetter instanceof Player){
    		 Player player=(Player)firstBetter;
    		 if(player.getChips()!=0){
    			 player.raise(getAnte()/2);
    		 }
    	 }
    	 else if(firstBetter instanceof AI){
    		 AI aiplayer=(AI)firstBetter;
    		 if(aiplayer.getChips()!=0){
    			 aiplayer.raise(getAnte()/2);
    		 }
    	 }
    	 
    	 ArrayList<Object> bettingList=bettingList(firstBetter);
    	 
    	 do{
    		 for(Object player:bettingList){
    			 if(player instanceof Player){
    				 Player player1=(Player)player;
    				 if(player1.getActive()&&player1.getLastBet()!=currentBet){
    					 player1.betDiffrence();
    				 }
    			 }
    			 else if(player instanceof AI){
    				 AI aiplayer=(AI)player;
    				 if(aiplayer.getActive()&&aiplayer.getLastBet()!=currentBet){
    					 aiplayer.betDiffrence();
    				 }
    			 }
    			
    		 }
    		 for(Object player:bettingList){
    			 if(player instanceof Player){
    				 Player player1=(Player)player;
    				 if(player1.hasBet()){
    					 player1.setActive();
    				 }
    			 }
    			 else if(player instanceof AI){
    				 AI aiplayer=(AI)player;
    				 if(aiplayer.hasBet()){
    					 aiplayer.setActive();
    				 }
    			 }
    		 }
    	 }
    	 while(!allBetEqual());
    	 
    	 currentBet=0;
    	 ArrayList<Object>AllPlayers=new ArrayList<Object>();
         for(Player player:players){
         	AllPlayers.add(player);
         }
         for(AI aiplayer:aiPlayers){
         	AllPlayers.add(aiplayer);
         }
    	 
    	 for(Object player:AllPlayers){
    		 if(player instanceof Player){
    			 Player player1=(Player)player;
    			 player1.resetLastBet();
    		 }
    		 else if(player instanceof AI){
    			 AI aiplayer=(AI)player;
    			 aiplayer.resetLastBet();
    		 }
    	 }
    	 
    	 
     }
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    
    
     public void endRound(Object winner){
             
             int winnings = currentPot;
             
          if(winner instanceof Player){
        	  Player player=(Player)winner;
        	  System.out.println(player.getName()+" has won the round with a " +player.getCurrentHand().rankingString()+" and gets "+winnings+" chips");
          }
          else if(winner instanceof AI){
        	  AI aiplayer=(AI)winner;
        	  System.out.println(aiplayer.getAIname()+" has won the round with a " +aiplayer.getCurrentHand().rankingString()+" and gets "+winnings+" chips");
          }
                  
             status = TableStatus.DONE;
     }
     
 
     private int arrayIsGreater(int[] a, int[] b){
           
             for(int i=0; i<a.length; i++){
                     if(a[i]>b[i]){
                             return 1;
                     } 
                     else if(a[i]<b[i]){
                             return -1;
                     }
             }
             //if the loop is exited it means the arrays are equal
             return 0;
     }

     public String playersHands(){
             String hands = "Players hands: ";
            
             ArrayList<Object> AllPlayers=new ArrayList<Object>();
             
             for(Player player:players){
            	 AllPlayers.add(player);
             }
             for(AI aiplayer:aiPlayers){
            	 AllPlayers.add(aiplayer);
             }
             
             
             for(Object player:AllPlayers){
            	 if(player instanceof Player){
            		 Player player1=(Player)player;
            		 hands=hands+ "\n" +player1.getName()+
            				 " \n\t Hand: "+player1.getCurrentHand();
            	 }
            	 else if(player instanceof AI){
            		 AI aiplayer=(AI)player;
            		 hands=hands+ "\n" +aiplayer.getAIname()+
            				 " \n\t Hand: "+aiplayer.getCurrentHand();
            	 }
             }
           
             return hands;
     }
     
     
   
     

}